﻿using System;
using System.Activities;
using System.Activities.Statements;
using System.Activities.Validation;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using WFExtended.Activities;
using WFExtended.Framework.Constraints;
using WFExtended.Framework.ServiceModel;

namespace WFExtended.Activities
{
    /// <summary>
    ///  Operation Invoker Activity Constraint
    /// </summary>
    /// <typeparam name="A">Type of the Operation Invoker Activity</typeparam>
    public class WcfOperationInvokerConstraint<A> : AConstraint<A>
        where A : AWcfOperationInvoker
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="isWarning">Define if the constraint is a warning</param>
        /// <param name="propertyName">Contraint property name</param>
        /// <param name="operationName">Operation name that to be validated</param>
        /// <param name="parameters">List of parameters of the operation that has been defined</param>
        public WcfOperationInvokerConstraint(bool isWarning, string propertyName, string operationName, Dictionary<string, Type> parameters)
            :base(isWarning, propertyName)
        {
            _operationName = operationName;
            _parameters = parameters;
        }

        private string _operationName;

        private Dictionary<string, Type> _parameters;

        /// <summary>
        /// Get the activity Constraint
        /// </summary>
        /// <returns>Return the activity Constraint</returns>
        public override System.Activities.Validation.Constraint<A> GetConstraint()
        {
            var currentActivity = new DelegateInArgument<A>();
            var validationContext = new DelegateInArgument<ValidationContext>();
            var errorMessage = new Variable<string>();
            var activities = new Variable<IEnumerable<Activity>>();

            return new Constraint<A>
            {
                Body = new ActivityAction<A, ValidationContext>
                {
                    Argument1 = currentActivity,
                    Argument2 = validationContext,

                    Handler = new Sequence
                    {
                        Variables =
                        {
                            errorMessage,
                            activities
                        },
                        Activities =
                        {
                            new Assign<IEnumerable<Activity>>
                            {
                                To = activities,
                                Value = new GetParentChain
                                {                                    
                                    ValidationContext = validationContext
                                }
                            },
                            new Assign<string>
                            {
                                To = errorMessage,
                                Value = new InArgument<string>((context)=> ValidateOperation(activities.Get(context)))
                            },
                            new AssertValidation() 
                            {
                                Assertion = new InArgument<bool>((context) => String.IsNullOrEmpty(errorMessage.Get(context))),
                                IsWarning = IsWarning,
                                Message = errorMessage,
                                PropertyName = PropertyName
                            }
                        },
                    }
                }
            };
        }

        private string ValidateOperation(IEnumerable<Activity> activities)
        {
            string messsage = string.Empty;

            foreach(Activity activity in activities)
            {
                if(activity is IWcfProxyActivity)
                {
                    messsage = String.Empty;

                    Type proxyType = ((IWcfProxyActivity)activity).ProxyType;

                    var operationContract = ServiceModelHelper.GetOperation(proxyType, _operationName);

                    if (operationContract == null)
                        return String.Format("There is no Operation Contract {0}", _operationName);

                    foreach (KeyValuePair<string, Type> currentParam in operationContract.Item2)
                    {
                        if (!_parameters.ContainsKey(currentParam.Key))
                            return String.Format("You must define the parameter {0} with {1} Type", currentParam.Key, currentParam.Value);
                        else if (_parameters[currentParam.Key] != currentParam.Value)
                            return String.Format("Wrong Type for parameter {0}", currentParam.Key);
                    }

                    if (String.IsNullOrEmpty(messsage))
                        return messsage;
                }
                    
            }


            return messsage;
        }

    }
}
